#include "funcstack.h"

#define PROCSELFMAPS_COUNT		512
#define BACKTRACE_FRAMES_COUNT		64
#define FUNCSTACK_OUTPUT_BUFSIZE	40960
#define MAXLEN_MODULE_NAME		256
#define MAXLEN_FUNC_NAME		256
#define MAXLEN_SOURCE_FILE_AND_LINE	256+1+10

#include "LOGS.h"

struct ProcSelfMaps
{
	char		*start ;
	char		*end ;
	char		module[ MAXLEN_MODULE_NAME + 1 ] ;
} ;

struct BackTraceFrame
{
	char			*addr ;
	char			*addr_conv ;
	struct ProcSelfMaps	*p_proc_self_maps ;
	char			func_name[ MAXLEN_FUNC_NAME + 1 ] ;
	char			source_file_and_line[ MAXLEN_SOURCE_FILE_AND_LINE + 1 ] ;
	int			conv_flag ;
	Dl_info			info ;
	int			dladdr_return ;
} ;

static int ReadProcSelfExe( char *module )
{
	int		nret = 0 ;
	
	nret = readlink( "/proc/self/exe" , module , MAXLEN_MODULE_NAME ) ;
	if( nret == -1 )
		return -1;
	
	return 0;
}

static int ReadProcSelfMaps( struct ProcSelfMaps *proc_self_maps_array , int proc_self_maps_array_size )
{
	FILE			*fp = NULL ;
	int			i ;
	struct ProcSelfMaps	*p_proc_self_maps = NULL ;
	char			buf[ 2000 + 1 ] ;
	char			*p = NULL ;
	
	fp = fopen( "/proc/self/maps" , "r" ) ;
	if( fp == NULL )
		return -1;
	
	for( i = 0 , p_proc_self_maps = proc_self_maps_array ; i < proc_self_maps_array_size ; i++ , p_proc_self_maps++ )
	{
		memset( buf , 0x00 , sizeof(buf) );
		if( fgets( buf , sizeof(buf)-1 , fp ) == NULL )
			break;
		if( buf[0] == '\0' )
			continue;
		if( buf[strlen(buf)-1] == '\n' )
			buf[strlen(buf)-1] = '\0' ;
		
		sscanf( buf , "%p-%p" , & (p_proc_self_maps->start) , & (p_proc_self_maps->end) );
		
		p = strchr( buf , '/' ) ;
		if( p )
		{
			strncpy( p_proc_self_maps->module , p , sizeof(p_proc_self_maps->module)-1 );
		}
	}
	
	fclose(fp);
	
	return i;
}

static struct ProcSelfMaps *GetProcSelfMapsPtr( struct ProcSelfMaps *proc_self_maps_array , int proc_self_maps_array_count , char *addr )
{
	int			i ;
	struct ProcSelfMaps	*p_proc_self_maps = NULL ;
	
	for( i = 0 , p_proc_self_maps = proc_self_maps_array ; i < proc_self_maps_array_count ; i++ , p_proc_self_maps++ )
	{
		if( p_proc_self_maps->start <= addr && addr <= p_proc_self_maps->end )
			return p_proc_self_maps;
	}
	
	return NULL;
}

static int ConvAddrToFileLine( struct BackTraceFrame *p_backtrace_frame )
{
	char		command[ 256 + 1 ] ;
	FILE		*fp = NULL ;
	int		len ;
	
	if( p_backtrace_frame->p_proc_self_maps->module[0] == '\0' )
		return -1;
	
	memset( command , 0x00 , sizeof(command) );
	snprintf( command , sizeof(command)-1 , "addr2line -C -e %s -f -i %p", p_backtrace_frame->p_proc_self_maps->module , p_backtrace_frame->addr_conv );
	fp = popen( command , "r" ) ;
	if( fp == NULL )
		return -2;
	
	fgets( p_backtrace_frame->func_name , sizeof(p_backtrace_frame->func_name) , fp );
	if( p_backtrace_frame->func_name[0] == '?' )
	{
		strcpy( p_backtrace_frame->func_name , "(UNKNOW)" );
	}
	else
	{
		len = strlen(p_backtrace_frame->func_name) - 1 ;
		if( p_backtrace_frame->func_name[len] == '\n' )
			p_backtrace_frame->func_name[len] = '\0' ;
	}
	
	fgets( p_backtrace_frame->source_file_and_line , sizeof(p_backtrace_frame->source_file_and_line) , fp );
	if( p_backtrace_frame->source_file_and_line[0] == '?' )
	{
		strcpy( p_backtrace_frame->source_file_and_line , "(UNKNOW)" );
	}
	else
	{
		len = strlen(p_backtrace_frame->source_file_and_line) - 1 ;
		if( p_backtrace_frame->source_file_and_line[len] == '\n' )
			p_backtrace_frame->source_file_and_line[len] = '\0' ;
	}
	
	pclose( fp );
	
	return 0;
}

char *DumpFuncStack()
{
	void			*backtrace_frame[ BACKTRACE_FRAMES_COUNT ] ;
	int			backtrace_frame_array_count ;
	struct BackTraceFrame	backtrace_frame_array[ BACKTRACE_FRAMES_COUNT ] ;
	struct BackTraceFrame	*p_backtrace_frame = NULL ;
	
	char			module[ MAXLEN_MODULE_NAME + 1 ] ;
	int			proc_self_maps_array_count ;
	struct ProcSelfMaps	proc_self_maps_array[ PROCSELFMAPS_COUNT ] ;
	
	int			i ;
	int			len ;
	
	char			*output_buf_base = NULL ;
	int			output_buf_size ;
	int			output_buf_len ;
	int			output_buf_remain_len ;
	
	int			nret = 0 ;
	
	backtrace_frame_array_count = backtrace( backtrace_frame , BACKTRACE_FRAMES_COUNT ) ;
	if( backtrace_frame_array_count <= 0 )
		return NULL;
	memset( backtrace_frame_array , 0x00 , sizeof(struct BackTraceFrame) * backtrace_frame_array_count );
	
	memset( module , 0x00 , sizeof(module) );
	nret = ReadProcSelfExe( module ) ;
	if( nret )
		return NULL;
	
	memset( proc_self_maps_array , 0x00 , sizeof(struct ProcSelfMaps) * PROCSELFMAPS_COUNT );
	proc_self_maps_array_count = ReadProcSelfMaps( proc_self_maps_array , PROCSELFMAPS_COUNT ) ;
	if( proc_self_maps_array_count < 0 )
		return NULL;
	
	output_buf_size = 0 ;
	for( i = 1 , p_backtrace_frame = backtrace_frame_array ; i < backtrace_frame_array_count ; i++ , p_backtrace_frame++ )
	{
		p_backtrace_frame->addr = backtrace_frame[i] ;
		p_backtrace_frame->addr_conv = p_backtrace_frame->addr ;
		
		p_backtrace_frame->p_proc_self_maps = GetProcSelfMapsPtr( proc_self_maps_array , proc_self_maps_array_count , p_backtrace_frame->addr ) ;
		if( p_backtrace_frame->p_proc_self_maps )
		{
			if( p_backtrace_frame->p_proc_self_maps->module[0] && strcmp( module , p_backtrace_frame->p_proc_self_maps->module ) != 0 )
				p_backtrace_frame->addr_conv = (char*)(p_backtrace_frame->addr-p_backtrace_frame->p_proc_self_maps->start) ;
			
			p_backtrace_frame->conv_flag = ConvAddrToFileLine( p_backtrace_frame ) ;
		}
		
		if( p_backtrace_frame->p_proc_self_maps && p_backtrace_frame->conv_flag == 0 )
		{
			output_buf_size += snprintf( NULL , 0
					, "#%-2d %16p %16p %s AT %s IN %s\n"
					, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , p_backtrace_frame->func_name , p_backtrace_frame->source_file_and_line , p_backtrace_frame->p_proc_self_maps->module ) ;
		}
		else
		{
			memset( & (p_backtrace_frame->info) , 0x00 , sizeof(Dl_info) );
			p_backtrace_frame->dladdr_return = dladdr( p_backtrace_frame->addr , & (p_backtrace_frame->info) ) ;
			if( p_backtrace_frame->dladdr_return != 0 )
			{
				output_buf_size += snprintf( NULL , 0
						, "#%-2d %16p %16p %s AT %s IN %s\n"
						, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , p_backtrace_frame->info.dli_sname , "(UNKNOW)" , p_backtrace_frame->info.dli_fname ) ;
			}
			else
			{
				output_buf_size += snprintf( NULL , 0
						, "#%-2d %16p %16p %s AT %s IN %s\n"
						, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , "(UNKNOW)" , "(UNKNOW)" , (p_backtrace_frame->p_proc_self_maps?p_backtrace_frame->p_proc_self_maps->module:"UNKNOW") ) ;
			}
		}
	}
	
	output_buf_base = (char*)malloc( output_buf_size + 2 ) ;
	if( output_buf_base == NULL )
		return NULL;
	memset( output_buf_base , 0x00 , output_buf_size + 2 );
	output_buf_len = 0 ;
	output_buf_remain_len = output_buf_size + 1 ;
	for( i = 1 , p_backtrace_frame = backtrace_frame_array ; i < backtrace_frame_array_count ; i++ , p_backtrace_frame++ )
	{
		if( p_backtrace_frame->p_proc_self_maps && p_backtrace_frame->conv_flag == 0 )
		{
			len = snprintf( output_buf_base+output_buf_len , output_buf_remain_len
					, "#%-2d %16p %16p %s AT %s IN %s\n"
					, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , p_backtrace_frame->func_name , p_backtrace_frame->source_file_and_line , p_backtrace_frame->p_proc_self_maps->module ) ;
		}
		else
		{
			if( p_backtrace_frame->dladdr_return != 0 )
			{
				len = snprintf( output_buf_base+output_buf_len , output_buf_remain_len
						, "#%-2d %16p %16p %s AT %s IN %s\n"
						, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , p_backtrace_frame->info.dli_sname , "(UNKNOW)" , p_backtrace_frame->info.dli_fname ) ;
			}
			else
			{
				len = snprintf( output_buf_base+output_buf_len , output_buf_remain_len
						, "#%-2d %16p %16p %s AT %s IN %s\n"
						, i , p_backtrace_frame->addr , p_backtrace_frame->addr_conv , "(UNKNOW)" , "(UNKNOW)" , (p_backtrace_frame->p_proc_self_maps?p_backtrace_frame->p_proc_self_maps->module:"UNKNOW") ) ;
			}
		}
		output_buf_len += len ;
		output_buf_remain_len -= len ;
	}
	
	return output_buf_base;
}

